﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace DynamicGeometry
{
    public abstract partial class Behavior : INotifyPropertyChanged
    {
        public static event Action<Behavior> NewBehaviorCreated;
        public static event Action<Behavior> BehaviorDeleted;

        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public BehaviorToolButton CreateToolButton()
        {
            return new BehaviorToolButton(this);
        }

        private static List<Behavior> mSingletons;
        public static List<Behavior> Singletons
        {
            get
            {
                if (mSingletons == null)
                {
                    mSingletons = InitializeBehaviors();
                }
                return mSingletons;
            }
        }

        private static List<Behavior> InitializeBehaviors()
        {
            List<Behavior> result = new List<Behavior>();
            Type basic = typeof(Behavior);

            foreach (Type t in basic.Assembly.GetTypes())
            {
                if (basic.IsAssignableFrom(t)
                    && !t.IsAbstract
                    && t.GetConstructor(new Type[0]) != null
                    && !t.HasAttribute<IgnoreAttribute>())
                {
                    Behavior instance = Activator.CreateInstance(t) as Behavior;
                    result.Add(instance);
                }
            }

            BehaviorOrderer.Order(result);

            return result;
        }

        public virtual string Category
        {
            get
            {
                return "Common";
            }
        }

        public static Behavior LookupByName(string b)
        {
            return Singletons.Where(x => x.Name == b).FirstOrDefault();
        }

        public abstract string Name
        {
            get;
        }

        public virtual string HintText
        {
            get
            {
                return "";
            }
        }

        public static double IconSize
        {
            get
            {
                return 32;
            }
        }

        public UIElement Icon
        {
            get
            {

                return CreateIcon();
            }
        }

        public virtual FrameworkElement CreateIcon()
        {
            return IconBuilder.BuildIcon()
                .Point(0.5, 0.5)
                .Canvas;
        }

        protected Drawing mDrawing;
        public virtual Drawing Drawing
        {
            get
            {
                return mDrawing;
            }
            set
            {
                if (mDrawing != null)
                {
                    mDrawing.OnAttachToCanvas -= mDrawing_OnAttachToCanvas;
                    mDrawing.OnDetachFromCanvas -= mDrawing_OnDetachFromCanvas;
                    Stopping();
                    Parent = null;
                }
                mDrawing = value;
                if (mDrawing != null)
                {
                    mDrawing.OnAttachToCanvas += mDrawing_OnAttachToCanvas;
                    mDrawing.OnDetachFromCanvas += mDrawing_OnDetachFromCanvas;
                    Started();
                    Parent = mDrawing.Canvas;
                }
            }
        }

        void mDrawing_OnAttachToCanvas(Canvas e)
        {
            Parent = e;
        }

        void mDrawing_OnDetachFromCanvas(Canvas e)
        {
            Parent = null;
        }

        private Canvas mParent;
        public virtual Canvas Parent
        {
            get
            {
                return mParent;
            }
            set
            {
                if (mParent != null)
                {
                    mParent.MouseWheel -= MouseWheel;
                    mParent.MouseLeftButtonDown -= SafeMouseDown;
                    mParent.MouseMove -= SafeMouseMove;
                    mParent.MouseLeftButtonUp -= SafeMouseUp;
                    mParent.KeyDown -= SafeKeyDown;
                    mParent.KeyUp -= SafeKeyUp;
                }
                mParent = value;
                if (mParent != null)
                {
                    mParent.MouseWheel += MouseWheel;
                    mParent.MouseLeftButtonDown += SafeMouseDown;
                    mParent.MouseMove += SafeMouseMove;
                    mParent.MouseLeftButtonUp += SafeMouseUp;
                    mParent.KeyDown += SafeKeyDown;
                    mParent.KeyUp += SafeKeyUp;
                }
            }
        }

        public virtual object PropertyBag
        {
            get
            {
                return null;
            }
        }

        public virtual void Started()
        {

        }

        public virtual void Stopping()
        {

        }

        public void Cancel()
        {
            Stopping();
            Started();
        }

        public virtual void AbortAndSetDefaultTool()
        {
            Stopping();
            Reset();
            Drawing.SetDefaultBehavior();
        }

        protected virtual void Reset()
        {
        }

        public static bool IsCtrlPressed()
        {
            return (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;
        }

        public virtual void KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == System.Windows.Input.Key.Escape)
            {
                AbortAndSetDefaultTool();
                e.Handled = true;
            }
        }

        public virtual void KeyUp(object sender, KeyEventArgs e)
        {
        }

        public virtual void MouseDown(object sender, MouseButtonEventArgs e)
        {
        }

        public virtual void MouseMove(object sender, MouseEventArgs e)
        {
        }

        public virtual void MouseUp(object sender, MouseButtonEventArgs e)
        {
        }

        void HandleException(Exception ex)
        {
            Drawing.RaiseError(this, ex);
        }

        protected bool errorHappened = false;
        void SafeKeyDown(object sender, KeyEventArgs e)
        {
            try
            {
                KeyDown(sender, e);
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void SafeKeyUp(object sender, KeyEventArgs e)
        {
            try
            {
                KeyUp(sender, e);
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        void SafeMouseDown(object sender, MouseButtonEventArgs e)
        {
            try
            {
                MouseDown(sender, e);
            }
            catch (Exception ex)
            {
                //errorHappened = true;
               // HandleException(ex);
            }
        }

        void SafeMouseMove(object sender, MouseEventArgs e)
        {
            if (errorHappened)
            {
                return;
            }
            try
            {
                MouseMove(sender, e);
            }
            catch (Exception ex)
            {
                HandleException(ex);
                errorHappened = true;
            }
        }

        void SafeMouseUp(object sender, MouseButtonEventArgs e)
        {
            errorHappened = false;
            try
            {
                MouseUp(sender, e);
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        public virtual void MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (Drawing != null)
            {
                if (e.Delta > 0)
                {
                    Drawing.CoordinateSystem.ZoomIn();
                }
                else if (e.Delta < 0)
                {
                    Drawing.CoordinateSystem.ZoomOut();
                }
            }
        }

        protected virtual FreePoint CreatePointAtCurrentPosition(
            Point coordinates,
            bool recordAction)
        {
            var result = Factory.CreateFreePoint(Drawing, coordinates);
            AddFigure(result, recordAction);
            return result;
        }

        /// <summary>
        /// Add figure to canvas or figures, depending on the record action parameter.
        /// </summary>
        /// <param name="figureToAdd">The figure to add</param>
        /// <param name="recordAction">Whether or not this action has to be recorded for
        /// use in a transaction (Used for undo/redo).</param>
        protected virtual void AddFigure(
            IFigure figureToAdd,
            bool recordAction)
        {
            if (recordAction)
            {
                Drawing.Add(figureToAdd);
            }
            else
            {
                Drawing.Figures.Add(figureToAdd);
            }
        }

        #region Coordinates

        protected virtual Point Coordinates(MouseEventArgs e)
        {
            var result = e.GetPosition(Parent).SnapToIntegers();
            result = result.Plus(new Point(0.5, 0.5));
            result = ToLogical(result);
            if (Settings.Instance.EnableSnapToPoint)
            {
                result = Math.GetSnapToPointPosition(1, result, new List<Point>(Drawing.Figures.ToPoints()), Settings.Instance.EnableSnapToGrid);
            }
            else if (Settings.Instance.EnableSnapToGrid)
            {
                result = Math.GetSnapToGridPosition(1, result);
            }
            return result;
        }


        protected double CursorTolerance
        {
            get
            {
                return Drawing.CoordinateSystem.CursorTolerance;
            }
        }

        protected double ToPhysical(double logicalLength)
        {
            return Drawing.CoordinateSystem.ToPhysical(logicalLength);
        }

        protected Point ToPhysical(Point point)
        {
            return Drawing.CoordinateSystem.ToPhysical(point);
        }

        protected double ToLogical(double pixelLength)
        {
            return Drawing.CoordinateSystem.ToLogical(pixelLength);
        }

        protected Point ToLogical(Point pixel)
        {
            return Drawing.CoordinateSystem.ToLogical(pixel);
        }

        #endregion

        public static void Add(Behavior newBehavior)
        {
            Behavior.Singletons.Add(newBehavior);
            if (Behavior.NewBehaviorCreated != null)
            {
                Behavior.NewBehaviorCreated(newBehavior);
            }
            ToolStorage.Instance.AddTool(newBehavior);
        }

        public static void Delete(Behavior tool)
        {
            Behavior.Singletons.Remove(tool);
            tool.Command.Observers.CommandRemoved();
            if (Behavior.BehaviorDeleted != null)
            {
                Behavior.BehaviorDeleted(tool);
            }
            ToolStorage.Instance.RemoveTool(tool);
        }

        public virtual void BehaviorSelected()
        {

        }

        public Command Command { get; set; }
    }
}
